<template>
  <!-- :style="{top: `${customBarHeight}px`, height: `calc(100% - ${customBarHeight}px)`} -->
  <view
    v-if="show"
    class="tt-sign-board-class tt-sign-board"
    :style="{ height: `calc(100% - ${customBarHeight}px)` }"
  >
    <!-- 签名canvas -->
    <view class="tt-sign-board__content">
      <view class="tt-sign-board__content__wrapper">
        <canvas
          class="tt-sign-board__content__canvas"
          :canvas-id="canvasName"
          :disableScroll="true"
          @touchstart="onTouchStart"
          @touchmove="onTouchMove"
          @touchend="onTouchEnd"
        ></canvas>
      </view>
    </view>

    <!-- 底部工具栏 -->
    <view class="tt-sign-board__tools">
      <!-- 可选颜色 -->
<!--      <view class="tt-sign-board__tools__color">-->
<!--        <view-->
<!--          v-for="(item, index) in signSelectColor"-->
<!--          :key="index"-->
<!--          class="tt-sign-board__tools__color__item"-->
<!--          :class="[-->
<!--            {-->
<!--              'tt-sign-board__tools__color__item&#45;&#45;active':-->
<!--                currentSelectColor === item,-->
<!--            },-->
<!--          ]"-->
<!--          :style="{ backgroundColor: item }"-->
<!--          @tap="colorSwitch(item)"-->
<!--        ></view>-->
<!--      </view>-->

      <!-- 按钮 -->
      <view class="tt-sign-board__tools__button">
        <view class="tt-sign-board__tools__button__item tt-bg-red" @tap="reDraw">清除</view>
        <view class="tt-sign-board__tools__button__item tt-bg-blue" @tap="save">保存</view>
<!--        <view class="tt-sign-board__tools__button__item tt-bg-indigo" @tap="previewImage">预览</view>-->
        <view class="tt-sign-board__tools__button__item tt-bg-orange" @tap="closeBoard">关闭</view>
      </view>
    </view>

    <!-- 伪全屏生成旋转图片canvas容器，不在页面上展示 -->
    <view
      style="
        position: fixed;
        left: -2000px;
        width: 0;
        height: 0;
        overflow: hidden;
      "
    >
      <canvas
        canvas-id="temp-tt-sign-canvas"
        :style="{ width: `${canvasHeight}px`, height: `${canvasHeight}px` }"
      ></canvas>
    </view>
  </view>
</template>

<script>
export default {
  name: 'tt-sign-board',
  props: {
    // 是否显示
    show: {
      type: Boolean,
      default: false,
    },
    // 可选签名颜色
    signSelectColor: {
      type: Array,
      default() {
        return ['#080808', '#E83A30']
      },
    },
    // 是否旋转输出图片
    rotate: {
      type: Boolean,
      default: true,
    },
    // 自定义顶栏的高度
    customBarHeight: {
      type: [String, Number],
      default: 0,
    },
  },
  data() {
    return {
      canvasName: 'tt-sign-canvas',
      ctx: null,
      canvasWidth: 0,
      canvasHeight: 0,
      currentSelectColor: this.signSelectColor[0],
      // 第一次触摸
      firstTouch: false,
      // 透明度
      transparent: 1,
      // 笔迹倍数
      lineSize: 1.5,
      // 最小画笔半径
      minLine: 0.5,
      // 最大画笔半径
      maxLine: 4,
      // 画笔压力
      pressure: 1,
      // 顺滑度，用60的距离来计算速度
      smoothness: 60,
      // 当前触摸的点
      currentPoint: {},
      // 当前线条
      currentLine: [],
      // 画笔圆半径
      radius: 1,
      // 裁剪区域
      cutArea: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
      },
      // 所有线条， 生成贝塞尔点
      // bethelPoint: [],
      // 上一个点
      lastPoint: 0,
      // 笔迹
      chirography: [],
      // 当前笔迹
      // currentChirography: {},
      // 画线轨迹，生成线条的实际点
      linePrack: [],
      uploadUrl: 'http://k-blockly.quwen.xyz/api/upload/base64',
    }
  },
  watch: {
    show(value) {
      if (value && this.canvasWidth === 0 && this.canvasHeight === 0) {
        this.$nextTick(() => {
          this.getCanvasInfo()
        })
      }
    },
    signSelectColor(value) {
      if (value.length > 0) {
        this.currentSelectColor = value[0]
      }
    },
  },
  created() {
    // 创建canvas
    this.ctx = uni.createCanvasContext(this.canvasName, this)
  },
  mounted() {
    // 获取画板的相关信息
    this.$nextTick(() => {
      this.getCanvasInfo()
    })
  },
  methods: {
    //
    _tGetRect(selector, all) {
      return new Promise((resolve) => {
        uni
          .createSelectorQuery()
          .in(this)
          [all ? 'selectAll' : 'select'](selector)
          .boundingClientRect((rect) => {
            if (all && Array.isArray(rect) && rect.length) {
              resolve(rect)
            }
            if (!all && rect) {
              resolve(rect)
            }
          })
          .exec()
      })
    },
    // 获取画板的相关信息
    getCanvasInfo() {
      this._tGetRect('.tt-sign-board__content__canvas').then((res) => {
        this.canvasWidth = res.width
        this.canvasHeight = res.height

        // 初始化Canvas
        this.$nextTick(() => {
          this.initCanvas('#FFFFFF')
        })
      })
    },
    // 初始化Canvas
    initCanvas(color) {
      /* 将canvas背景设置为 白底，不设置  导出的canvas的背景为透明 */
      // rect() 参数说明  矩形路径左上角的横坐标，左上角的纵坐标, 矩形路径的宽度, 矩形路径的高度
      // 矩形的宽高需要减去边框的宽度
      this.ctx.rect(
        0,
        0,
        this.canvasWidth - uni.upx2px(4),
        this.canvasHeight - uni.upx2px(4)
      )
      this.ctx.setFillStyle(color)
      this.ctx.fill()
      this.ctx.draw()
    },
    // 开始画
    onTouchStart(e) {
      if (e.type != 'touchstart') return false

      // 设置线条颜色
      this.ctx.setFillStyle(this.currentSelectColor)
      // 设置透明度
      this.ctx.setGlobalAlpha(this.transparent)
      let currentPoint = {
        x: e.touches[0].x,
        y: e.touches[0].y,
      }
      let currentLine = this.currentLine
      currentLine.unshift({
        time: new Date().getTime(),
        dis: 0,
        x: currentPoint.x,
        y: currentPoint.y,
      })
      this.currentPoint = currentPoint

      if (this.firstTouch) {
        this.cutArea = {
          top: currentPoint.y,
          right: currentPoint.x,
          bottom: currentPoint.y,
          left: currentPoint.x,
        }
        this.firstTouch = false
      }

      this.pointToLine(currentLine)
    },
    // 正在画
    onTouchMove(e) {
      if (e.type != 'touchmove') return false
      if (e.cancelable) {
        // 判断默认行为是否已经被禁用
        if (!e.defaultPrevented) {
          e.preventDefault()
        }
      }
      let point = {
        x: e.touches[0].x,
        y: e.touches[0].y,
      }

      if (point.y < this.cutArea.top) {
        this.cutArea.top = point.y
      }
      if (point.y < 0) this.cutArea.top = 0

      if (point.x < this.cutArea.right) {
        this.cutArea.right = point.x
      }
      if (this.canvasWidth - point.x <= 0) {
        this.cutArea.right = this.canvasWidth
      }
      if (point.y > this.cutArea.bottom) {
        this.cutArea.bottom = this.canvasHeight
      }
      if (this.canvasHeight - point.y <= 0) {
        this.cutArea.bottom = this.canvasHeight
      }
      if (point.x < this.cutArea.left) {
        this.cutArea.left = point.x
      }
      if (point.x < 0) this.cutArea.left = 0

      this.lastPoint = this.currentPoint
      this.currentPoint = point

      let currentLine = this.currentLine
      currentLine.unshift({
        time: new Date().getTime(),
        dis: this.distance(this.currentPoint, this.lastPoint),
        x: point.x,
        y: point.y,
      })

      this.pointToLine(currentLine)
    },
    // 移动结束
    onTouchEnd(e) {
      if (e.type != 'touchend') return false
      let point = {
        x: e.changedTouches[0].x,
        y: e.changedTouches[0].y,
      }
      this.lastPoint = this.currentPoint
      this.currentPoint = point

      let currentLine = this.currentLine
      currentLine.unshift({
        time: new Date().getTime(),
        dis: this.distance(this.currentPoint, this.lastPoint),
        x: point.x,
        y: point.y,
      })

      //一笔结束，保存笔迹的坐标点，清空，当前笔迹
      //增加判断是否在手写区域
      this.pointToLine(currentLine)
      let currentChirography = {
        lineSize: this.lineSize,
        lineColor: this.currentSelectColor,
      }

      let chirography = this.chirography
      chirography.unshift(currentChirography)
      this.chirography = chirography

      let linePrack = this.linePrack
      linePrack.unshift(this.currentLine)
      this.linePrack = linePrack
      this.currentLine = []
    },
    // 重置绘画板
    reDraw() {
      this.initCanvas('#FFFFFF')
      this.$emit('rest')
    },
    // 保存
    save() {
      let _this = this
      // 在组件内使用需要第二个参数this
      uni.canvasToTempFilePath(
        {
          canvasId: this.canvasName,
          fileType: 'png',
          quality: 1,
          success: (res) => {
            if (this.rotate) {
              this.getRotateImage(res.tempFilePath)
                .then(async(base64) => {
                  //储存图片地址
                  let imgPath
                  // try {
                  //   let ret= await  _this.$customHttp.post(_this.uploadUrl,{base64})
                  //   imgPath = ret.数据.地址
                  // }catch (e) {
                  //     console.error('上传图片错误',e)
                  // }
                  this.$emit('confirm', base64,imgPath)
                })
                .catch((err) => {
                  this.$toasttone('旋转图片失败')
                })
            } else {
              this.$emit('save', res.tempFilePath)
            }
          },
          fail: () => {
            this.$toasttone('保存失败')
          },
        },
        this
      )
    },
    $toasttone(msg) {
      uni.showToast({
        title: msg,
        icon: 'none',
      })
    },
    // 预览图片
    previewImage() {
      // 在组件内使用需要第二个参数this
      uni.canvasToTempFilePath(
        {
          canvasId: this.canvasName,
          fileType: 'png',
          quality: 1,
          success: (res) => {
            if (this.rotate) {
              this.getRotateImage(res.tempFilePath)
                .then((res) => {
                  uni.previewImage({
                    urls: [res],
                  })
                })
                .catch((err) => {
                  this.$toasttone('旋转图片失败')
                })
            } else {
              uni.previewImage({
                urls: [res.tempFilePath],
              })
            }
          },
          fail: (e) => {
            this.$toasttone('预览失败')
          },
        },
        this
      )
    },
    // 关闭签名板
    closeBoard() {
      this.$emit('closed')
    },
    // 切换画笔颜色
    colorSwitch(color) {
      this.currentSelectColor = color
    },
    // 绘制两点之间的线条
    pointToLine(line) {
      this.calcBethelLine(line)
    },
    // 计算插值，让线条更加圆滑
    calcBethelLine(line) {
      if (line.length <= 1) {
        line[0].r = this.radius
        return
      }
      let x0,
        x1,
        x2,
        y0,
        y1,
        y2,
        r0,
        r1,
        r2,
        len,
        lastRadius,
        dis = 0,
        time = 0,
        curveValue = 0.5
      if (line.length <= 2) {
        x0 = line[1].x
        y0 = line[1].y
        x2 = line[1].x + (line[0].x - line[1].x) * curveValue
        y2 = line[1].y + (line[0].y - line[1].y) * curveValue
        x1 = x0 + (x2 - x0) * curveValue
        y1 = y0 + (y2 - y0) * curveValue
      } else {
        x0 = line[2].x + (line[1].x - line[2].x) * curveValue
        y0 = line[2].y + (line[1].y - line[2].y) * curveValue
        x1 = line[1].x
        y1 = line[1].y
        x2 = x1 + (line[0].x - x1) * curveValue
        y2 = y1 + (line[0].y - y1) * curveValue
      }
      // 三个点分别是(x0,y0),(x1,y1),(x2,y2) ；(x1,y1)这个是控制点，控制点不会落在曲线上；实际上，这个点还会手写获取的实际点，却落在曲线上
      len = this.distance(
        {
          x: x2,
          y: y2,
        },
        {
          x: x0,
          y: y0,
        }
      )
      lastRadius = this.radius
      for (let i = 0; i < line.length - 1; i++) {
        dis += line[i].dis
        time += line[i].time - line[i + 1].time
        if (dis > this.smoothness) break
      }

      this.radius =
        Math.min((time / len) * this.pressure + this.minLine, this.maxLine) *
        this.lineSize
      line[0].r = this.radius
      // 计算笔迹半径
      if (line.length <= 2) {
        r0 = (lastRadius + this.radius) / 2
        r1 = r0
        r2 = r1
      } else {
        r0 = (line[2].r + line[1].r) / 2
        r1 = line[1].r
        r2 = (line[1].r + line[0].r) / 2
      }
      let n = 5
      let point = []
      for (let i = 0; i < n; i++) {
        let t = i / (n - 1)
        let x = (1 - t) * (1 - t) * x0 + 2 * t * (1 - t) * x1 + t * t * x2
        let y = (1 - t) * (1 - t) * y0 + 2 * t * (1 - t) * y1 + t * t * y2
        let r = lastRadius + ((this.radius - lastRadius) / n) * i
        point.push({
          x,
          y,
          r,
        })
        if (point.length === 3) {
          let a = this.ctaCalc(
            point[0].x,
            point[0].y,
            point[0].r,
            point[1].x,
            point[1].y,
            point[1].r,
            point[2].x,
            point[2].y,
            point[2].r
          )
          a[0].color = this.currentSelectColor

          this.drawBethel(a, true)
          point = [
            {
              x,
              y,
              r,
            },
          ]
        }
      }
      this.currentLine = line
    },
    // 求两点之间的距离
    distance(a, b) {
      let x = b.x - a.x
      let y = b.y - a.y
      return Math.sqrt(x * x + y * y)
    },
    // 计算点信息
    ctaCalc(x0, y0, r0, x1, y1, r1, x2, y2, r2) {
      let a = [],
        vx01,
        vy01,
        norm,
        n_x0,
        n_y0,
        vx21,
        vy21,
        n_x2,
        n_y2
      vx01 = x1 - x0
      vy01 = y1 - y0
      norm = Math.sqrt(vx01 * vx01 + vy01 * vy01 + 0.0001) * 2
      vx01 = (vx01 / norm) * r0
      vy01 = (vy01 / norm) * r0
      n_x0 = vy01
      n_y0 = -vx01
      vx21 = x1 - x2
      vy21 = y1 - y2
      norm = Math.sqrt(vx21 * vx21 + vy21 * vy21 + 0.0001) * 2
      vx21 = (vx21 / norm) * r2
      vy21 = (vy21 / norm) * r2
      n_x2 = -vy21
      n_y2 = vx21
      a.push({
        mx: x0 + n_x0,
        my: y0 + n_y0,
        color: '#080808',
      })
      a.push({
        c1x: x1 + n_x0,
        c1y: y1 + n_y0,
        c2x: x1 + n_x2,
        c2y: y1 + n_y2,
        ex: x2 + n_x2,
        ey: y2 + n_y2,
      })
      a.push({
        c1x: x2 + n_x2 - vx21,
        c1y: y2 + n_y2 - vy21,
        c2x: x2 - n_x2 - vx21,
        c2y: y2 - n_y2 - vy21,
        ex: x2 - n_x2,
        ey: y2 - n_y2,
      })
      a.push({
        c1x: x1 - n_x2,
        c1y: y1 - n_y2,
        c2x: x1 - n_x0,
        c2y: y1 - n_y0,
        ex: x0 - n_x0,
        ey: y0 - n_y0,
      })
      a.push({
        c1x: x0 - n_x0 - vx01,
        c1y: y0 - n_y0 - vy01,
        c2x: x0 + n_x0 - vx01,
        c2y: y0 + n_y0 - vy01,
        ex: x0 + n_x0,
        ey: y0 + n_y0,
      })
      a[0].mx = a[0].mx.toFixed(1)
      a[0].mx = parseFloat(a[0].mx)
      a[0].my = a[0].my.toFixed(1)
      a[0].my = parseFloat(a[0].my)
      for (let i = 1; i < a.length; i++) {
        a[i].c1x = a[i].c1x.toFixed(1)
        a[i].c1x = parseFloat(a[i].c1x)
        a[i].c1y = a[i].c1y.toFixed(1)
        a[i].c1y = parseFloat(a[i].c1y)
        a[i].c2x = a[i].c2x.toFixed(1)
        a[i].c2x = parseFloat(a[i].c2x)
        a[i].c2y = a[i].c2y.toFixed(1)
        a[i].c2y = parseFloat(a[i].c2y)
        a[i].ex = a[i].ex.toFixed(1)
        a[i].ex = parseFloat(a[i].ex)
        a[i].ey = a[i].ey.toFixed(1)
        a[i].ey = parseFloat(a[i].ey)
      }
      return a
    },
    // 绘制贝塞尔曲线
    drawBethel(point, is_fill, color) {
      this.ctx.beginPath()
      this.ctx.moveTo(point[0].mx, point[0].my)
      if (color != undefined) {
        this.ctx.setFillStyle(color)
        this.ctx.setStrokeStyle(color)
      } else {
        this.ctx.setFillStyle(point[0].color)
        this.ctx.setStrokeStyle(point[0].color)
      }
      for (let i = 1; i < point.length; i++) {
        this.ctx.bezierCurveTo(
          point[i].c1x,
          point[i].c1y,
          point[i].c2x,
          point[i].c2y,
          point[i].ex,
          point[i].ey
        )
      }
      this.ctx.stroke()
      if (is_fill != undefined) {
        //填充图形 ( 后绘制的图形会覆盖前面的图形, 绘制时注意先后顺序 )
        this.ctx.fill()
      }
      this.ctx.draw(true)
    },
    // 旋转图片
    async getRotateImage(dataUrl) {
      let _this = this
      // const url = await this.base64ToPath(dataUrl)
      const url = dataUrl

      // 创建新画布
      const tempCtx = uni.createCanvasContext('temp-tt-sign-canvas', this)
      const width = this.canvasWidth
      const height = this.canvasHeight
      tempCtx.restore()
      tempCtx.save()
      tempCtx.translate(0, height)
      tempCtx.rotate((270 * Math.PI) / 180)
      tempCtx.drawImage(url, 0, 0, width, height)
      tempCtx.draw()
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          uni.canvasToTempFilePath(
            {
              canvasId: 'temp-tt-sign-canvas',
              fileType: 'png',
              x: 0,
              y: height - width,
              width: height,
              height: width,
              success: (res) => resolve(res.tempFilePath),
              fail: reject,
            },
            _this
          )
        }, 150)
      })
    },
    // 将base64转换为本地
    base64ToPath(dataUrl) {
      return new Promise((resolve, reject) => {
        // 判断地址是否包含bas64字样，不包含直接返回
        if (dataUrl.indexOf('base64') !== -1) {
          const data = uni.base64ToArrayBuffer(
            dataUrl.replace(/^data:image\/\w+;base64,/, '')
          )
          // #ifdef MP-WEIXIN
          const filePath = `${
            wx.env.USER_DATA_PATH
          }/${new Date().getTime()}-${Math.random().toString(32).slice(2)}.png`
          // #endif
          // #ifndef MP-WEIXIN
          const filePath = `${new Date().getTime()}-${Math.random()
            .toString(32)
            .slice(2)}.png`
          // #endif
          uni.getFileSystemManager().writeFile({
            filePath,
            data,
            encoding: 'base64',
            success: () => resolve(filePath),
            fail: reject,
          })
        } else {
          resolve(dataUrl)
        }
      })
    },
  },
}
</script>

<style lang="scss" scoped>
.tt-bg-red {
  background-color: #e83a30;
}
.tt-bg-blue {
  background-color: #3d7eff;
}
.tt-bg-indigo {
  background-color: #31c9e8;
}
.tt-bg-orange {
  background-color: #ffa726;
}

.tt-sign-board {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  background-color: #e6e6e6;
  z-index: 997;
  display: flex;
  flex-direction: row-reverse;

  &__content {
    width: 84%;
    height: 100%;

    &__wrapper {
      width: calc(100% - 60rpx);
      height: calc(100% - 60rpx);
      margin: 30rpx;
      border-radius: 20rpx;
      border: 2rpx dotted #aaaaaa;
      overflow: hidden;
    }

    &__canvas {
      width: 100%;
      height: 100%;
      background-color: #ffffff;
    }
  }

  &__tools {
    width: 16%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;

    &__color {
      margin-top: 30rpx;

      &__item {
        width: 70rpx;
        height: 70rpx;
        border-radius: 100rpx;
        margin: 20rpx auto;

        &--active {
          position: relative;

          &::after {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            width: 40%;
            height: 40%;
            border-radius: 100rpx;
            background-color: #ffffff;
            transform: translate(-50%, -50%);
          }
        }
      }
    }

    &__button {
      margin-bottom: 30rpx;
      display: flex;
      flex-direction: column;

      &__item {
        width: 130rpx;
        height: 60rpx;
        line-height: 60rpx;
        text-align: center;
        margin: 60rpx auto;
        border-radius: 10rpx;
        color: #ffffff;
        transform-origin: center center;
        transform: rotateZ(90deg);
      }
    }
  }
}
</style>
